package name.nano.client;



import java.util.ArrayList;

import name.nano.client.dto.LocationInTime;
import name.nano.client.enums.GameMode;
import name.nano.client.utils.Coordinate;
import name.nano.client.utils.Vector;



public class UltimatePlayer {
	Coordinate m_location;
	Coordinate m_startLocation;
	ArrayList<LocationInTime> m_targetLocationList=new ArrayList<LocationInTime>();
	Vector m_currentRunningDirection=new Vector(0,0);
	GameMode m_gameMode;
	private final String m_color;
	private UltimatePlayer m_opponentForDefence;
	private final FrisbeeTacticBoard m_simulator;
	int m_currentTargetIndex=0;
	private long m_lastMilliseconds=0;
	
	private boolean m_breaking=false;
	private boolean m_targetReached=false;
	int m_maxSpeed=6; //meters per second
	float m_currentSpeed=0; //meters per second
	//int m_acc=1; // meter per square second

	public UltimatePlayer(FrisbeeTacticBoard simulator,Coordinate location, GameMode team, String color) {
		super();
		this.m_simulator=simulator;
		this.m_location = location;
		this.m_startLocation=new Coordinate(location.x,location.y);
		this.m_gameMode=team;
		this.m_color=color;
	}
	
	public Coordinate getLocation() {
		return m_location;
	}

	public boolean hasDisc() {
		return m_simulator.getPlayerWithDisc()==this;
	}

	public void setLocation(Coordinate location) {
		this.m_location = location;
	}

	public void addTargetLocation(LocationInTime targetLoc){
		this.m_targetLocationList.add(targetLoc);
	}
	public LocationInTime getNextTargetLocation(){
		
		
		
		if (this.m_targetLocationList!=null && m_currentTargetIndex<this.m_targetLocationList.size()){
			
			LocationInTime locationInTime = this.m_targetLocationList.get(m_currentTargetIndex);
			return locationInTime;
			
		}
		return null; 
		
	}
	
	public void setManDefenceOpponent(UltimatePlayer offencePlayer){
		this.m_opponentForDefence=offencePlayer;
	}
	private void calculateCurrentTargetLocation(long milliseconds) {
		LocationInTime result=null;
		
		if (m_opponentForDefence!=null && m_gameMode==GameMode.DEFENCE){
			float dx=0;
			float dy=0;
			UltimatePlayer playerWithDisc = this.m_simulator.getPlayerWithDisc();
			
			Coordinate opponentLocation = m_opponentForDefence.getLocation();
			if (m_opponentForDefence==playerWithDisc){
				switch (m_simulator.getDefenceMode()){
				case LEFT_OPEN:
					dx=1;
					dy=-1;
					break;
				case RIGHT_OPEN:
					dx=-1;
					dy=-1;
					break;
				}
				
				
				result= new LocationInTime(opponentLocation.x + dx,opponentLocation.y+dy,0);
			} else {
				Coordinate diskLocation=playerWithDisc.m_location;
				Vector v=getVector(new Coordinate(m_opponentForDefence.m_location.x+m_opponentForDefence.m_currentRunningDirection.dx,m_opponentForDefence.m_location.y+m_opponentForDefence.m_currentRunningDirection.dy), diskLocation, 3); //2meter in front of the opponent
				int reactionSpeed = 200;
				if (getCurrentSpeed()>0)
					reactionSpeed=0;
				result= new LocationInTime(opponentLocation.x+v.dx,opponentLocation.y+v.dy,milliseconds+reactionSpeed);
				//result.m_calculated=true;
			}

			if (result!=null){
				double distance=result.distance(this.m_location);
				if (distance>=0.5){
					//this.m_targetLocationList.clear();
					this.m_targetLocationList.add(result);
					//m_currentTargetIndex=0;
					System.out.println("distance:"+distance +" dv="+(m_currentSpeed-m_opponentForDefence.m_currentSpeed));
				} else {
					//System.out.println("own location:"+m_location+" targetLocation:"+m_opponentForDefence.getLocation());
				}
			}
			if (this.m_targetLocationList.size()>1){
				for (int i=m_targetLocationList.size()-1;i>m_currentTargetIndex;i--){
					if (m_targetLocationList.get(i).seconds<milliseconds)
						m_currentTargetIndex=i;
				}
			}
	
		}
		
		
	}
	public Vector getVector(Coordinate p1,Coordinate p2, double distanceFromP1){
		double dx=p2.x-p1.x;
		double dy=p2.y-p1.y;
		double dist=Math.sqrt(dx*dx+dy*dy);
		if (dist>0){
			double f=distanceFromP1/dist;
			return new Vector(f*dx,f*dy);
		} else {
			return new Vector(0,0);
		}
		
	}
	public double getAngle(Coordinate p1,Coordinate p2){
		double dx=p2.x-p1.x;
		double dy=p2.y-p1.y;
		double degree = 90+ Math.toDegrees(Math.atan(dy/dx));
		if (dx<0)
			degree=degree+180;
		return degree;
	}
	public boolean nextStep(long milliseconds) {
		calculateCurrentTargetLocation(milliseconds);
		
		LocationInTime targetLocation = getNextTargetLocation();
		boolean moreWork=false;
		
		
		if (targetLocation!=null ){
			double distance = targetLocation.distance(m_location);
			moreWork=distance>0  || (m_breaking && m_currentSpeed>0);
			if (targetLocation.seconds<=milliseconds){
				
				long deltaMS = milliseconds-m_lastMilliseconds;
				if (m_breaking) {
					if (m_currentSpeed>0){
						m_currentSpeed-=10* (getAcceleration()*(deltaMS/1000f));
						m_currentSpeed=Math.max(m_currentSpeed,0);
					}
				} else {
					if (m_currentSpeed<getMaxSpeed()){
						m_currentSpeed+=getAcceleration()*(deltaMS/1000f);
						m_currentSpeed=Math.min(m_currentSpeed,getMaxSpeed());
					}
				}
				
				double meters=(deltaMS*(getCurrentSpeed()/(double)1000));
				
				if (distance>0){
					if (meters>distance &&!m_targetReached){
						//meters=distance;
						m_targetReached=true;
					}
				} else {
					m_targetReached=true;
				}
				Coordinate dragPosition = new Coordinate(m_location.x+m_currentRunningDirection.dx,m_location.y+m_currentRunningDirection.dy);
				if (m_targetReached){
					if (m_targetLocationList.size()>m_currentTargetIndex+1){
						//there are other points
						double angle = getAngle(m_location,m_targetLocationList.get(m_currentTargetIndex+1));
						double angle2 = getAngle(m_location,dragPosition);
						double dAngle = Math.abs(angle2-angle);
						if (dAngle>180)
							dAngle=Math.abs(dAngle-360);
						if (dAngle>50){
							m_breaking=true;
						}
						//System.out.println("angle"+dAngle);
					} else {
						m_breaking=true;
					}
					
				}
				if (!m_breaking)
					m_currentRunningDirection=getVector(m_location, targetLocation, meters);
				else {
					
					m_currentRunningDirection=getVector(m_location, dragPosition, meters);
				}
				//double angle2 = getAngle(m_location,dragPosition);
				//System.out.println("angle"+angle2);
				this.m_location.x=this.m_location.x +  m_currentRunningDirection.dx;
				this.m_location.y=this.m_location.y +  m_currentRunningDirection.dy;

			}
			
		}
		m_lastMilliseconds=milliseconds;

		if (targetLocation!=null && m_targetReached && (m_currentSpeed<1||!m_breaking)){
				m_currentTargetIndex++;	
				m_targetReached=false;
				if (m_breaking){
					m_currentSpeed=0;
					m_breaking=false;
				}
		}
		
		return moreWork;
	}

	private float getMaxSpeed() {
		return m_opponentForDefence!=null?m_maxSpeed+1:m_maxSpeed;
	}

	private float getAcceleration() {
		return Math.max(0,getMaxSpeed()-Math.abs(getCurrentSpeed()));
	}

	private float getCurrentSpeed() {
		return m_currentSpeed;
	}

	public String getColor() {
		if (hasDisc())
			return "#FF3030";
		return m_color;
	}

	public void addWaypoint(LocationInTime waipoint) {
		m_targetLocationList.add(waipoint);		
	}

	public ArrayList<LocationInTime> getRunningPoints() {
		return this.m_targetLocationList;
	}

	public void setCurrentRunningIndex(int i) {
		this.m_currentTargetIndex=0;
		this.m_location=new Coordinate(m_startLocation.x,m_startLocation.y);
		m_lastMilliseconds=0;
	}
	
}
